package stream;

import org.junit.Test;
import stream.vo.Dish;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamTest {

    public static void main(String[] args) {
        List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH) );

        List<Dish> collect = menu.stream()
                .filter(d -> d.getCalories() < 400)
                .sorted(Comparator.comparing(Dish::getCalories))
                .collect(Collectors.toList());
        System.out.println(collect);
    }

    @Test
    public void distinctTest() {
        List<Integer> list = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        list.stream().distinct().forEach(System.out::println);
    }

    @Test
    public void limitTest() {
        List<Integer> list = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        list.stream().limit(3).forEach(System.out::println);
    }

    @Test
    public void skipTest() {
        AtomicInteger i = new AtomicInteger(0);
        Stream<Integer> stream = Stream.generate(() -> i.incrementAndGet());

        int skip = 100;
        int limit = 10;

        stream.skip(skip).limit(limit).forEach(System.out::println);
    }

    @Test
    public void filter(){
        IntStream.rangeClosed(1, 10000).parallel().filter(n -> n % 2 == 0).forEach(System.out::println);
    }

    @Test
    public void map(){
        List<String> list = Stream.of("tu", "you").map(String::toUpperCase).collect(Collectors.toList());
        System.out.println(list);

        List<Integer> list2 = Stream.of(1, 2, 3).map(n -> n * n).collect(Collectors.toList());
        System.out.println(list2);
    }

    @Test
    public void flatMapTest() {
        String[] strs = new String[]{"tu", "you"};
        Arrays.stream(strs)
                .map(i -> i.split(""))
                .flatMap(Arrays::stream)
                .forEach(System.out::println);
    }

    @Test
    public void flatMapTest2() {
        List<Integer> l1 = Arrays.asList(1, 2, 4);
        List<Integer> l2 = Arrays.asList(5, 6);

        l1.stream()
                .flatMap(i -> l2.stream().map(j -> new int[]{i, j}))
                .collect(Collectors.toList())
                .forEach(item -> System.out.println(Arrays.toString(item)));
    }

    /**
     * 方便记录日志
     */
    @Test
    public void peek(){
        IntStream intStream = IntStream.rangeClosed(1, 10);
        intStream.peek(e -> System.out.println(e)).map(i -> i * i).forEach(i -> {});
    }



    /**
     * 演示两次终结操作，这样会报错
     */
    @Test
    public void terminalTwice(){
        IntStream intStream = IntStream.rangeClosed(1, 10);
        intStream.forEach(System.out::println);
        intStream.forEach(System.out::println);
    }

    @Test
    public void reduce(){
        OptionalInt result = IntStream.range(0, 10).reduce(Integer::sum);
        result.ifPresent(System.out::println);

        int result2 = IntStream.range(0, 10).reduce(0, Integer::sum);
        System.out.println(result2);

        Optional<String> concat = Stream.of("A", "B", "C", "D").reduce(String::concat);
        concat.ifPresent(System.out::println);
    }

    /**
     * 聚合函数
     */
    @Test
    public void minMaxDistinct(){
        IntStream inputStream = IntStream.of(25,231,2,5,2,3434,121);
//        inputStream.min().ifPresent(System.out::println);
//        inputStream.max().ifPresent(System.out::println);
        System.out.println(inputStream.count());
//        inputStream.distinct().forEach(System.out::println);
    }

    @Test
    public void match(){
        IntStream inputStream = IntStream.of(25,231,2,5,2,3434,121);
//        System.out.println(inputStream.allMatch(a -> a > 0));
//        System.out.println(inputStream.anyMatch(a -> a > 231));
        System.out.println(inputStream.noneMatch(a -> a > 3434));
    }

    /**
     * findAny在并发的性能更高
     */
    @Test
    public void find(){
        IntStream inputStream = IntStream.of(25,231,2,5,2,3434,121);
//        inputStream.parallel().findFirst().ifPresent(System.out::println);
        inputStream.parallel().findAny().ifPresent(System.out::println);
    }

    /**
     * 生成无限流
     */
    @Test
    public void generate(){
        Random seed = new Random();
        Supplier<Integer> random = seed::nextInt;
        Stream.generate(random).limit(10).forEach(System.out::println);
    }

    /**
     * 生成无限流，相比于有了个初始值和迭代索引
     */
    @Test
    public void iterate(){
        Stream.iterate(0, n -> n + 3)
                .limit(10)
                .forEach(System.out::println);
    }

    public class Person{
        private int age;
        public Person(int age){
            this.age = age;
        }

        public int getAge() {
            return age;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    '}';
        }
    }

    @Test
    public void groupingBy(){
        Stream<Person> inputStream = Stream.of(
                new Person(1),
                new Person(1),
                new Person(2),
                new Person(2));
        Map<Integer, List<Person>> result = inputStream.collect(Collectors.groupingBy(Person::getAge));
        System.out.println(result);
    }

    /**
     * 分成两组，true的一组，false的一组
     */
    @Test
    public void partitioningBy(){
        Stream<Person> inputStream = Stream.of(
                new Person(25),
                new Person(231),
                new Person(2),
                new Person(5),
                new Person(2),
                new Person(3434),
                new Person(121));
        Map<Boolean, List<Person>> result = inputStream.collect(Collectors.partitioningBy(p -> p.getAge() < 10));
        System.out.println(result);
    }
}
